package main

import (
    "net"
    "log"
    "unsafe"
    "io"
    "math/rand"
)

type Cell struct {
    genome [64]uint8
    r, g, b uint8
    age uint32
    energy, minerals int32
    direction int32
    genomeIndex uint8
    type_ int32
    green, red, blue uint32
    useMineral uint32
}

type pkgType int32

const (
    P_AUTH pkgType = iota
    P_SEND
    P_CLOSE
)

type Package struct {
    cell Cell
    x, y int32
    type_ pkgType
}

var (
    clients []net.Conn
)

const (
    PKG_SIZE = int(unsafe.Sizeof(Package{}))
)

func main() {
    log.Println("Launching server...")
    ln, err := net.Listen("tcp", ":8081")
	if(err != nil) {
		log.Fatal(err)
	}
    for {
        conn, err := ln.Accept()
        if(err != nil) {
            log.Print(err)
            continue
        }
        go serve(conn)
    }
}

func serve(conn net.Conn) {
    defer conn.Close()
    ip := conn.RemoteAddr().String()
    log.Print(ip, " connected.")
    clients = append(clients, conn)
    for {
        message := make([]byte, 1 << 20)
        n, err := conn.Read(message);
        if(err != nil) {
            if(err != io.EOF) {
                log.Print(err)
            }
            break
        }
        if(n >= PKG_SIZE) {
            for i := 0; i < n; i += PKG_SIZE {
                data := (*(*Package)(unsafe.Pointer(&message[i:n][0])))
                if(data.type_ == P_AUTH) {
                    continue
                } else if(data.type_ == P_CLOSE) {
                    err := conn.Close()
                    if(err != nil) {
                        log.Println(err)
                    }
                    break
                }
                processData(data, conn)
            }
        }
    }
    clients = remove(clients, conn)
    log.Print(ip, " disconnected.")
}

func remove(l []net.Conn, item net.Conn) []net.Conn {
    for i, other := range l {
        if other.RemoteAddr().String() == item.RemoteAddr().String() {
            return append(l[:i], l[i+1:]...)
        }
    }
    return l
}

func processData(pkg Package, conn net.Conn) {
    client := conn
	client = clients[rand.Intn(len(clients))]
    data := (*(*[1<<31 - 1]byte)(unsafe.Pointer(&pkg)))[:PKG_SIZE]
    client.Write(data)
}
